/**
前提:
已知字符串元素类型为 item_t

情况:
is_xalloc 是一个复杂的复合定义
只要符合如下定义的类型，is_xalloc 都为 true，这里将该类型叫做 mix_alloc
- mix_alloc 符合 can_stream 约束，也就是 mix_alloc 能转通过 stream_wrapper<item_t> 或 stream_wrapperx<item_t> 进行构造操作
    或者本身就是 stream_wrapper<item_t> 或 stream_wrapperx<item_t>。如果传入 item_t ** 指针，则符合从 stream_wrapper<item_t> 构造。
    item_t buf[128];
    item_t * ptr = buf;
    也就是说，假如你认为缓冲区足够，可以直接传 &ptr（二级指针，内部会修改这个指针，最终会让它指向最后一个字符）
    但它没有长度约束，不会检查缓冲区溢出，这不是个好的写法。这里实现它只是为了支持本来就是 stream_wrapper<item_t> 和 stream_wrapperx<item_t> 类型的 stream，
    而非它们隐式转换的构造函数参数类型，比如 stream_wrapper<item_t> 支持从 item_t ** 类型构造，
    那么也就间接让 item_t ** 符合 is_xalloc 中的类型约束

- mix_alloc 符合 is_alloc 约束，也就是 mix_alloc 是一个 lambda，签名如下
    using lambda_t = item_t * (uxx length);
    也就是支持给 mix_alloc 传入期望的缓冲区长度，然后返回 length 个元素的 item_t * 指针，
    这是很久之前继承下来的接口，在本模块出现之前只支持这种回调

- mix_alloc 符合 can_alloc_stream 约束，这个结构就像套娃一样，签名如下
    case 1:
        using lambda_t = __batches_lambda(uxx length);
        通过向 mix_alloc 传入期望的缓冲区长度，然后返回一个 __batches_lambda stream 对象，这个流支持 output() 接口用于将内容写入缓冲区，
        __batches_lambda 对外暴露的接口和 inc::stream_wrapper<item_t> 一致，但对于 user 来说，不要直接使用 __batches_lambda
                                                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        __batches_lambda 本身指向一个缓冲区，缓冲区大小固定为 inc::limit::lang_cxx_private_cxx_helper，需要在构造时确保这一点，
        缓冲区写满了才会执行回调写入操作，然后重复使用缓冲区
    case 2 & 3:
        using lambda_t = inc::stream_wrapper<item_t>(uxx length);
        using lambda_t = inc::stream_wrapperx<item_t>(uxx length);
        都是通过传入期望的缓冲区长度，然后返回一个 stream 对象，
        因为是流式的，所以无法确定字符串最终会有多长，所以就目前而言 length 参数会被忽略（同样适用于 case 1）
- mix_alloc 本身就为 __batches_lambda 的情况，仅供内部使用
*/
#ifndef xpack_lang_cxx_private_cxx_helper
#define xpack_lang_cxx_private_cxx_helper
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::lang_cxx_private_cxx_helper::inc
#include"configure/limit.hpp"
#include"define/base_type.hpp"
#include"dumb/disable_copy.hpp"
#include"interface/can_alloc.hpp"
#include"interface/can_callback.hpp"
#include"macro/xalign.hpp"
#include"macro/xexport.hpp"
#include"macro/xis_nullptr.hpp"
#include"math/minimum.hpp"
#include"meta/has_cast.hpp"
#include"meta/is_same.hpp"
#include"meta/origin_of.hpp"
#include"memop/copy.hpp"
#include"memop/fill.hpp"
#include"utils/mix_buffer.hpp"
#include"utils/stream_wrapper.hpp"
#pragma pop_macro("xuser")

namespace mixc::lang_cxx_private_cxx_helper::origin{
    template<class item_t>
    struct cxx_helper{
        using the_t = cxx_helper<item_t>;
        struct __batches_lambda;

        // inline item_t empty = item_t(0); 新版 gcc 存在编译警告
        static inline item_t empty[2] = {};

        template<class lambda_t>
        static constexpr bool can_stream = 
            inc::can_stream<lambda_t, item_t>;
        
        template<class lambda_t>
        static constexpr bool can_alloc_stream =
            inc::can_callback<lambda_t, struct __batches_lambda(uxx)> or
            inc::can_callback<lambda_t, inc::stream_wrapper<item_t>(uxx)> or
            inc::can_callback<lambda_t, inc::stream_wrapperx<item_t>(uxx)>;
        
        template<class lambda_t>
        static constexpr bool is_alloc = inc::can_alloc<lambda_t, item_t>;
        
        template<class lambda_t>
        static constexpr bool is_stream_or_alloc = can_stream<lambda_t> or is_alloc<lambda_t>;

        template<class lambda_t, class origin_lambda_t = inc::origin_of<lambda_t>>
        static constexpr bool is_xalloc =
        #ifdef xfor_vscode_hint
            true;
        #else
            is_stream_or_alloc<origin_lambda_t> or
            can_alloc_stream<origin_lambda_t> or
            inc::is_same<__batches_lambda, origin_lambda_t>;
        #endif

        // 放到外边定义，避免代码膨胀
        //
        // 禁止拷贝，因为 core::buffer 在调用 inc::stream_wrapper<item_t> operator()(uxx length) 后
        // 会被 inc::stream_wrapper<item_t> 指向，需要确保始终指向同一个变量
        struct alloc_lambda : inc::disable_copy{
        private:
            inc::ialloc<item_t>   alloc;    // 不能使用引用类型，因为 alloc 是局部的
            mutable item_t      * buffer;
            mutable item_t     ** p_ptr;
            mutable uxx         * p_length;
        public:
            // 这里把 m_ptr 和 m_length 作为参数传入，避免误操作，因为不一定是设置当前对象的这些字段
            // 一般只有构造时传入 alloc_t 是需要设置当前对象的 m_ptr 和 的 m_length
            // 像其他函数，可能会通过 alloc_t 复制
            // 注意:
            // alloc_t 不能直接使用 inc::istream_raw<item_t> 代替，
            // 因为此封装补充设置了 m_ptr 和 m_length，避免出现
            alloc_lambda(auto const & alloc, item_t ** p_ptr, uxx * p_length):
                alloc(alloc), p_ptr(p_ptr), p_length(p_length){
            }

            inc::stream_wrapper<item_t> operator()(uxx length) const {
                // 需要维持 buffer 的生命周期
                // 因为此函数返回 inc::stream_wrapper<item_t> 对象后，
                // 仍需要一个指针指向当前缓冲区写入的位置，这样就能保证
                buffer          = alloc(length);

                xunlikely(buffer == nullptr){
                    *p_ptr      = &empty[0];
                    *p_length   = 0;
                }
                else{
                    *p_ptr      = buffer;
                    *p_length   = length;
                }
                return xmodify(buffer);
            }
        };

        template<class lambda_t, class cxx_t>
        requires(inc::can_alloc<lambda_t, item_t>)
        static auto stream_helper(lambda_t const & alloc, cxx_t * self){
            return stream_helper(alloc, xmodify(self->m_ptr), xmodify(self->m_length));
        }

        template<class lambda_t>
        requires(inc::can_alloc<lambda_t, item_t>)
        static auto stream_helper(lambda_t const & alloc, item_t ** p_ptr, uxx * p_length){
            return alloc_lambda(alloc, p_ptr, p_length);
        };

        template<class can_alloc_stream_t>
        requires(can_alloc_stream<can_alloc_stream_t>)
        static auto stream_helper(can_alloc_stream_t const & alloc_stream, item_t **, uxx *){
            using lambda_t          = decltype(alloc_stream(0));
            struct xalign(alignof(lambda_t)) core{
            private:
                static auto configure(){
                    if constexpr (inc::has_cast<__batches_lambda, lambda_t>){
                        return __batches_lambda{};
                    }
                    else if constexpr (inc::has_cast<inc::stream_wrapper<item_t>, lambda_t>){
                        return inc::stream_wrapper<item_t>{};
                    }
                    else{
                        return inc::stream_wrapperx<item_t>{};
                    }
                }

                can_alloc_stream_t  const & alloc_stream;

                // 由于 stream lambda 是作为返回值，需要延长 lambda 的生命周期
                mutable u08         dummy[sizeof(lambda_t)];
                mutable bool        is_invoked;
            public:
                core(can_alloc_stream_t const & alloc_stream) : alloc_stream(alloc_stream), is_invoked(false){}
                ~core(){
                    if (is_invoked){
                        ((lambda_t *)(dummy))->~lambda_t();
                    }
                }

                decltype(configure()) operator()(uxx length) const {
                    xnew(&dummy) lambda_t(alloc_stream(length));
                    is_invoked      = true;
                    return *(lambda_t *)(dummy);
                }
            };
            return core(alloc_stream);
        }

        // 放到外边定义，避免代码膨胀
        template<class stream_wrapper_t>
        struct alloc_stream_lambda{
        private:
            stream_wrapper_t stream;
        public:
            alloc_stream_lambda(stream_wrapper_t const & stream) : stream(stream){}

            stream_wrapper_t operator()(uxx) const {
                return stream;
            }
        };

        template<class lambda_t>
        requires(can_stream<lambda_t>)
        static auto stream_helper(lambda_t const & stream, voidp){
            return stream_helper(stream, nullptr, nullptr);
        }

        template<class lambda_t>
        requires(inc::has_cast<inc::stream_wrapper<item_t>, lambda_t>)
        // 需要确保 stream 的作用域和生命周期，不能使用 stream_t(引用类型)
        static auto stream_helper(lambda_t const & stream, item_t **, uxx *){
            return alloc_stream_lambda<inc::stream_wrapper<item_t>>(stream);
        }

        template<class lambda_t>
        requires(inc::has_cast<inc::stream_wrapperx<item_t>, lambda_t>)
        // 需要确保 stream 的作用域和生命周期，不能使用 stream_t(引用类型)
        // 不能使用如下方式，这里用 inc::stream_warpper<item_t> const & 指示 stream_t
        // 因为 lambda 转换成 stream 引用类型，这个 stream 是一个局部变量，
        // 假如 alloc_stream_lambda<> 内部也使用 stream_t 引用类型再指向 stream，
        // 那么在 alloc_stream_lambda<> 返回时，stream 就失效了
        // 不过当 lambda 本身就是 inc::stream_warpper<item_t> 时则无此问题
        // static auto stream_helper(inc::stream_warpper<item_t> const & stream, item_t **, uxx *);
        static auto stream_helper(lambda_t const & stream, item_t **, uxx *){
            return alloc_stream_lambda<inc::stream_wrapperx<item_t>>(stream);
        }

        // 不能使用这个版本，因为 inc::can_stream_helper 会获取 lambda 的 this 指针，而这个 lambda 是局部变量
        // 当函数返回时就失效了
        // static inc::istream_raw<item_t> stream_helper(alloc_t alloc, item_t ** p_ptr, uxx * p_length){
        //     return inc::can_stream_helper([&](uxx length) -> item_t * {
        //     });
        // }

        // 注意:
        // buffer 缓冲区的大小必须和 inc::limit::lang_cxx_private_cxx_helper 一致
        // user 不要直接使用此接口，库中用到此定义的函数是为了内部使用
        struct __batches_lambda{
            using func_t    = void(item_t const * content, uxx length);
            using invoke_t  = inc::icallback<func_t>;
            mutable invoke_t    invoke;
            mutable item_t *    buffer;
            mutable item_t **   pptr;

            void output(item_t const * content, uxx length) const {
                output_core((item_t *)content, length);
            }

            void output(item_t const & value) const {
                output_core((item_t *)&value, 1);
            }

            void output(item_t const & value, uxx total_count) const {
                output_core2(total_count, [&](auto buffer, uxx count){
                    inc::fill_with_operator(buffer, value, count);
                });
            }

            void finish() const {
                if (auto rest = *pptr - buffer; rest){
                    *pptr           = buffer;
                    invoke(buffer, rest);
                }
            }

            xis_nullptr(invoke == nullptr);
        private:
            void output_core2(uxx length, auto && set) const {
                for(auto & ptr = *pptr;;){
                    auto rest       = inc::limit::lang_cxx_private_cxx_helper - uxx(ptr - buffer);
                    auto count      = inc::minimum<uxx>(rest, length);
                    set(ptr, count);

                    if (ptr += count; ptr - buffer != inc::limit::lang_cxx_private_cxx_helper){
                        return;
                    }

                    invoke(buffer, inc::limit::lang_cxx_private_cxx_helper);
                    ptr             = buffer;
                    length         -= count;
                }
            }

            void output_core(item_t const * content, uxx length) const {
                output_core2(length, [&](auto buffer, uxx count){
                    inc::copy_with_operator_unsafe(buffer, content, count);
                    content        += count;
                });
            }
        };

        static auto stream_helper(__batches_lambda const & stream, voidp){
            return stream_helper(stream, nullptr, nullptr);
        }

        static auto stream_helper(__batches_lambda const & stream, item_t **, uxx *){
            return alloc_stream_lambda<__batches_lambda/*不能使用引用，lang/cxx/format.hpp 中存在冲突*/>(stream);
        }

        template<bool is_stream_v>
        static void in_batches(auto const & alloc, auto const & core){
            item_t buffer[inc::limit::lang_cxx_private_cxx_helper];
            auto ptr                = buffer;

            if constexpr (is_stream_v){
                auto stream_inner   = alloc(0);
                auto output         = [&](item_t const * content, uxx length){
                    stream_inner.output(content, length);
                };
                auto stream         = __batches_lambda{ .invoke = output, .buffer = buffer, .pptr = &ptr };
                core(stream);
                stream.finish();
            }
            else{ // 不能以流的形式读取，那么必须一次性计算出最终长度，然后写入到缓冲区中
                auto mix_buffer     = inc::mix_buffer<item_t>{};
                auto output         = [&](item_t const * content, uxx length){
                    mix_buffer.push(content, length);
                };
                auto stream         = __batches_lambda{ .invoke = output, .buffer = buffer, .pptr = &ptr };
                core(stream);       // 先
                stream.finish();    // 后

                auto length         = uxx(mix_buffer->length);
                auto real_stream    = alloc(length); // 后

                if (real_stream != nullptr){
                    mix_buffer.foreach_level([&](item_t const * buffer, uxx length){
                        real_stream.output(buffer, length);
                    });
                }
            }
        }
    };
}

#endif

xexport_space(mixc::lang_cxx_private_cxx_helper::origin)
